<?php


namespace common\components\form;

use Yii;
use yii\base\Widget;
use yii\helpers\Html;
use yii\helpers\Json;
use yii\helpers\ArrayHelper;
use yii\widgets\ActiveFormAsset;
use common\components\Helper;

/**
 * description of Form
 *
 * @author FireLoong
 */
class Form extends Widget
{
    const VALIDATION_STATE_ON_CONTAINER = 'container';
    const VALIDATION_STATE_ON_INPUT = 'input';
    public $action = '';
    public $method = 'post';
    public $options = [];
    public $layout = 'default';
    public $enableClientScript = true;
    public $validationUrl;
    public $validateOnSubmit = true;
    public $validateOnChange = true;
    public $validateOnBlur = true;
    public $validateOnType = false;
    public $validationDelay = 500;
    public $fieldNamespace;
    public $fieldConfig = [];
    public $encodeErrorSummary = true;
    public $errorSummaryCssClass = 'error-summary';
    public $requiredCssClass = 'required';
    public $errorCssClass = 'has-error';
    public $successCssClass = 'has-success';
    public $validatingCssClass = 'validating';
    public $validationStateOn = self::VALIDATION_STATE_ON_CONTAINER;
    public $enableClientValidation = true;
    public $enableAjaxValidation = false;
    public $ajaxParam = 'ajax';
    public $ajaxDataType = 'json';
    public $scrollToError = true;
    public $scrollToErrorOffset = 0;
    public $attributes = [];
    private $_fields = [];
    public $assets;

    public function init()
    {
        if (!in_array($this->layout, ['default', 'horizontal', 'inline'])) {
            $this->layout = 'default';
        }

        if ($this->layout !== 'default') {
            Html::addCssClass($this->options, 'form-' . $this->layout);
        }

        parent::init();
        if (!isset($this->options['id'])) {
            $this->options['id'] = $this->getId();
        }
        ob_start();
        ob_implicit_flush(false);
    }

    public function run()
    {
        if (!empty($this->_fields)) {
            throw new InvalidCallException('Each beginField() should have a matching endField() call.');
        }

        $content = ob_get_clean();
        $html = Html::beginForm($this->action, $this->method, $this->options);
        $html .= $content;

        if ($this->enableClientScript) {
            $this->registerClientScript();
        }

        $html .= Html::endForm();
        $this->setAssets();
        Yii::$app->cache->delete(\common\assets\AppAsset::CACHE_BUNDLE_KEY);
        return $html;
    }

    public function field($model, $attribute, $type, $options = [])
    {
        $config = $this->fieldConfig;
        if ($config instanceof \Closure) {
            $config = call_user_func($config, $model, $attribute);
        }
        if (!isset($config['class'])) {
            $config['class'] = $this->loadField($type);
        }

        $fieldObject = Yii::createObject(ArrayHelper::merge($config, $options, [
            'model' => $model,
            'attribute' => $attribute,
            'form' => $this,
            'type' => $type
        ]));

        return $fieldObject->setup();
    }

    public function xmlField($model, $attribute, \SimpleXMLElement $field, $options = [])
    {
        $config = $this->fieldConfig;
        if ($config instanceof \Closure) {
            $config = call_user_func($config, $model, $field);
        }
        if (!isset($config['class'])) {
            $config['class'] = $this->loadField((string)$field->attributes()->type);
        }

        $fieldObject = Yii::createObject(ArrayHelper::merge($config, $options, [
            'model' => $model,
            'attribute' => $attribute,
            'form' => $this,
            'type' => (string)$field->attributes()->type,
            //'setupParams' => $field 移除name、label、type、description、default等属性，其他属性都是setupParams参数
        ]));

        return $fieldObject->xmlSetup($field, $options);
    }

    public function xField($model, \SimpleXMLElement $field, $options = [], $attribute = 'params')
    {
        $config = $this->fieldConfig;
        if ($config instanceof \Closure) {
            $config = call_user_func($config, $model, $field);
        }
        if (!isset($config['class'])) {
            $config['class'] = $this->loadField((string)$field->attributes()->type);
        }

        $fieldObject = Yii::createObject(ArrayHelper::merge($config, $options, [
            'model' => $model,
            'attribute' => $attribute . '[' . (string)$field->attributes()->name . ']',
            'form' => $this,
            'type' => (string)$field->attributes()->type,
            //'setupParams' => $field 移除name、label、type、description、default等属性，其他属性都是setupParams参数
        ]));

        unset($options['layout']);

        return $fieldObject->xSetup($field, $options);
    }

    protected function loadField($type)
    {
        $class = __NAMESPACE__ . '\\field\\' . ucfirst($type);
        if (!empty($this->fieldNamespace)) {
            if (is_string($this->fieldNamespace)) {
                $this->fieldNamespace = [$this->fieldNamespace];
            }
            foreach ($this->fieldNamespace as $fieldNamespace) {
                $namespace = str_replace('/', '\\', trim($fieldNamespace, '\\/.'));
                $fieldClass = $namespace . '\\' . ucfirst($type);
                if (class_exists($fieldClass)) {
                    $class = $fieldClass;
                }
            }
        }

        if (class_exists($class)) {
            return $class;
        } else {
            return __NAMESPACE__ . '\field\Unknown';
        }
    }

    protected function setAssets()
    {
        if ($this->assets) {
            $view = $this->getView();
            foreach ($this->assets as $asset) {
                if (isset($asset['css'])) {
                    Helper::addAssetCssFile($view, $asset['sourcePath'] ?? null, $asset['css'], $asset['bundles'] ?? []);
                }
                if (isset($asset['js'])) {
                    Helper::addAssetJsFile($view, $asset['sourcePath'] ?? null, $asset['js'], $asset['bundles'] ?? []);
                }
            }
        }
    }

    public function registerClientScript()
    {
        $id = $this->options['id'];
        $options = Json::htmlEncode($this->getClientOptions());
        $attributes = Json::htmlEncode($this->attributes);
        $view = $this->getView();
        ActiveFormAsset::register($view);
        $view->registerJs("jQuery('#$id').yiiActiveForm($attributes, $options);");
    }

    protected function getClientOptions()
    {
        $options = [
            'encodeErrorSummary' => $this->encodeErrorSummary,
            'errorSummary' => '.' . implode('.', preg_split('/\s+/', $this->errorSummaryCssClass, -1, PREG_SPLIT_NO_EMPTY)),
            'validateOnSubmit' => $this->validateOnSubmit,
            'errorCssClass' => $this->errorCssClass,
            'successCssClass' => $this->successCssClass,
            'validatingCssClass' => $this->validatingCssClass,
            'ajaxParam' => $this->ajaxParam,
            'ajaxDataType' => $this->ajaxDataType,
            'scrollToError' => $this->scrollToError,
            'scrollToErrorOffset' => $this->scrollToErrorOffset,
            'validationStateOn' => $this->validationStateOn,
        ];
        if ($this->validationUrl !== null) {
            $options['validationUrl'] = Url::to($this->validationUrl);
        }

        // only get the options that are different from the default ones (set in yii.activeForm.js)
        return array_diff_assoc($options, [
            'encodeErrorSummary' => true,
            'errorSummary' => '.error-summary',
            'validateOnSubmit' => true,
            'errorCssClass' => 'has-error',
            'successCssClass' => 'has-success',
            'validatingCssClass' => 'validating',
            'ajaxParam' => 'ajax',
            'ajaxDataType' => 'json',
            'scrollToError' => true,
            'scrollToErrorOffset' => 0,
            'validationStateOn' => self::VALIDATION_STATE_ON_CONTAINER,
        ]);
    }

    public function genXmlFields($fields, $model, $langCat, $attribute = 'params')
    {
        $fieldHtml = '';
        $params = json_decode($model->$attribute, true) ?? $model->$attribute;
        foreach ($fields as $field) {
            $fieldName = (string)$field->attributes()->name;
            $fieldType = (string)$field->attributes()->type;
            $fieldLabel = (string)$field->attributes()->label;
            if (empty($fieldName) || empty($fieldType)) {
                $fieldHtml .= '';
            } else {
                if ((string)$field->attributes()->hiddenLabel === 'true') {
                    $label = false;
                } else {
                    $label = is_null($langCat) ? $fieldLabel : Yii::t($langCat, $fieldLabel);
                }
                $htmlLayout = $label === false ? 'default' : 'horizontal';

                if ($field->option) {
                    $options = [];
                    foreach ($field->option as $option) {
                        $options[(string)$option->attributes()->value] = (string)$option;
                    }
                    $setupParams = isset($params[$fieldName]) ? [$options, ['value' => $params[$fieldName]]] : [$options];
                    $fieldHtml .= $this->field($model, $attribute . '[' . $fieldName . ']', $fieldType, [
                        'layout' => $htmlLayout,
                        'setupParams' => $setupParams
                    ])->label($label);
                } else {
                    $setupParams = isset($params[$fieldName]) ? [['value' => $params[$fieldName]]] : [];
                    $fieldHtml .= $this->field($model, $attribute . '[' . $fieldName . ']', $fieldType, [
                        'layout' => $htmlLayout,
                        'setupParams' => $setupParams
                    ])->label($label);
                }
            }
        }
        return $fieldHtml;
    }
}
